home *** CD-ROM | disk | FTP | other *** search
/ Amiga Tools 2 / Amiga Tools 2.iso / tools / jade / src / find.c < prev    next >
C/C++ Source or Header  |  1995-03-09  |  25KB  |  910 lines

  1. /* find.c -- Searching and replacing
  2.    Copyright (C) 1993, 1994 John Harper <jsh@ukc.ac.uk>
  3.  
  4.    This file is part of Jade.
  5.  
  6.    Jade is free software; you can redistribute it and/or modify it
  7.    under the terms of the GNU General Public License as published by
  8.    the Free Software Foundation; either version 2, or (at your option)
  9.    any later version.
  10.  
  11.    Jade is distributed in the hope that it will be useful, but
  12.    WITHOUT ANY WARRANTY; without even the implied warranty of
  13.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.    GNU General Public License for more details.
  15.  
  16.    You should have received a copy of the GNU General Public License
  17.    along with Jade; see the file COPYING.    If not, write to
  18.    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  19.  
  20. #include "jade.h"
  21. #include "jade_protos.h"
  22. #include "regexp/regexp.h"
  23.  
  24. #include <string.h>
  25. #include <ctype.h>
  26. #include <stdlib.h>
  27.  
  28. _PR bool findnext(TX *, u_char *, POS *, bool);
  29. _PR bool findprev(TX *, u_char *, POS *, bool);
  30. _PR bool findstrnext(TX *, u_char *, POS *);
  31. _PR bool findstrprev(TX *, u_char *, POS *);
  32. _PR bool findcharnext(TX *, u_char, POS *);
  33. _PR bool findcharprev(TX *, u_char, POS *);
  34. _PR bool replaceit(TX *, u_char *, u_char *, POS *, bool);
  35. _PR bool replaceitstr(TX *, u_char *, u_char *, POS *);
  36. _PR bool mystrcmp(u_char *, u_char *);
  37. _PR u_char *mystrrstrn(u_char *, u_char *, int);
  38. _PR u_char *mystrrchrn(u_char *, u_char, int);
  39. _PR void find_init(void);
  40.  
  41. /* Storage for remembering where the last match was.
  42.    LAST_STRING is the string that was matched against, only it's address is
  43.    used.
  44.    LAST_START and LAST_END is copied straight out of the regexp struct
  45.    LAST_LINE is either -1 meaning the string was not in a buffer, or the
  46.    line number it came from.  */
  47. static u_char *last_string;
  48. static u_char *last_start[NSUBEXP];
  49. static u_char *last_end[NSUBEXP];
  50. static long last_line;
  51.  
  52. static VALUE sym_regexp_error;
  53.  
  54. static void
  55. update_last_match(u_char *str, regexp *prog, long line)
  56. {
  57.     last_string = str;
  58.     memcpy(last_start, (u_char **)prog->startp, sizeof(last_start));
  59.     memcpy(last_end, (u_char **)prog->endp, sizeof(last_end));
  60.     last_line = line;
  61. }
  62.  
  63. _PR VALUE cmd_find_next_regexp(VALUE re, VALUE pos, VALUE tx, VALUE nocase_p);
  64. DEFUN("find-next-regexp", cmd_find_next_regexp, subr_find_next_regexp, (VALUE re, VALUE pos, VALUE tx, VALUE nocase_p), V_Subr4, DOC_find_next_regexp) /*
  65. ::doc:find_next_regexp::
  66. find-next-regexp REGEXP [POS] [BUFFER] [IGNORE-CASE-P]
  67.  
  68. Scans forwards from POS (or the cursor), in BUFFER, looking for a match
  69. with REGEXP. Returns the position of the next match or nil.
  70.  
  71. When IGNORE-CASE-P is non-nil the case of matched strings are ignored. Note
  72. that character classes are still case-significant.
  73. ::end:: */
  74. {
  75.     POS res;
  76.     DECLARE1(re, STRINGP);
  77.     if(POSP(pos))
  78.     res = VPOS(pos);
  79.     else
  80.     res = curr_vw->vw_CursorPos;
  81.     if(!BUFFERP(tx))
  82.     tx = VAL(curr_vw->vw_Tx);
  83.     check_pos(VTX(tx), &res);
  84.     if(findnext(VTX(tx), VSTR(re), &res, !NILP(nocase_p)))
  85.     return(make_lpos(&res));
  86.     return(sym_nil);
  87. }
  88.  
  89. _PR VALUE cmd_find_prev_regexp(VALUE re, VALUE pos, VALUE tx, VALUE nocase_p);
  90. DEFUN("find-prev-regexp", cmd_find_prev_regexp, subr_find_prev_regexp, (VALUE re, VALUE pos, VALUE tx, VALUE nocase_p), V_Subr4, DOC_find_prev_regexp) /*
  91. ::doc:find_prev_regexp::
  92. find-prev-regexp REGEXP [POS] [BUFFER] [IGNORE-CASE-P]
  93.  
  94. Scans backwards from POS (or the cursor), in BUFFER, looking for a match
  95. with REGEXP. Returns the position of the next match or nil.
  96.  
  97. When IGNORE-CASE-P is non-nil the case of matched strings are ignored. Note
  98. that character classes are still case-significant.
  99. ::end:: */
  100. {
  101.     POS res;
  102.     DECLARE1(re, STRINGP);
  103.     if(POSP(pos))
  104.     res = VPOS(pos);
  105.     else
  106.     res = curr_vw->vw_CursorPos;
  107.     if(!BUFFERP(tx))
  108.     tx = VAL(curr_vw->vw_Tx);
  109.     check_pos(VTX(tx), &res);
  110.     if(findprev(VTX(tx), VSTR(re), &res, !NILP(nocase_p)))
  111.     return(make_lpos(&res));
  112.     return(sym_nil);
  113. }
  114.  
  115. _PR VALUE cmd_find_next_string(VALUE str, VALUE pos, VALUE tx);
  116. DEFUN("find-next-string", cmd_find_next_string, subr_find_next_string, (VALUE str, VALUE pos, VALUE tx), V_Subr3, DOC_find_next_string) /*
  117. ::doc:find_next_string::
  118. find-next-string STRING [POS] [BUFFER]
  119.  
  120. Scans forwards from POS (or the cursor), in BUFFER, looking for a match
  121. with STRING. Returns the position of the next match or nil.
  122. ::end:: */
  123. {
  124.     POS res;
  125.     DECLARE1(str, STRINGP);
  126.     if(POSP(pos))
  127.     res = VPOS(pos);
  128.     else
  129.     res = curr_vw->vw_CursorPos;
  130.     if(!BUFFERP(tx))
  131.     tx = VAL(curr_vw->vw_Tx);
  132.     check_pos(VTX(tx), &res);
  133.     if(findstrnext(VTX(tx), VSTR(str), &res))
  134.     return(make_lpos(&res));
  135.     return(sym_nil);
  136. }
  137.  
  138. _PR VALUE cmd_find_prev_string(VALUE str, VALUE pos, VALUE tx);
  139. DEFUN("find-prev-string", cmd_find_prev_string, subr_find_prev_string, (VALUE str, VALUE pos, VALUE tx), V_Subr3, DOC_find_prev_string) /*
  140. ::doc:find_prev_string::
  141. find-prev-string STRING [POS] [BUFFER]
  142.  
  143. Scans backwards from POS (or the cursor), in BUFFER, looking for a match
  144. with STRING. Returns the position of the next match or nil.
  145. ::end:: */
  146. {
  147.     POS res;
  148.     DECLARE1(str, STRINGP);
  149.     if(POSP(pos))
  150.     res = VPOS(pos);
  151.     else
  152.     res = curr_vw->vw_CursorPos;
  153.     if(!BUFFERP(tx))
  154.     tx = VAL(curr_vw->vw_Tx);
  155.     check_pos(VTX(tx), &res);
  156.     if(findstrprev(VTX(tx), VSTR(str), &res))
  157.     return(make_lpos(&res));
  158.     return(sym_nil);
  159. }
  160.  
  161. _PR VALUE cmd_find_next_char(VALUE ch, VALUE pos, VALUE tx);
  162. DEFUN("find-next-char", cmd_find_next_char, subr_find_next_char, (VALUE ch, VALUE pos, VALUE tx), V_Subr3, DOC_find_next_char) /*
  163. ::doc:find_next_char::
  164. find-next-char CHAR [POS] [BUFFER]
  165.  
  166. Scans forwards from POS (or the cursor), in BUFFER, looking for a match
  167. with CHAR. Returns the position of the next match or nil.
  168. ::end:: */
  169. {
  170.     POS res;
  171.     DECLARE1(ch, CHARP);
  172.     if(POSP(pos))
  173.     res = VPOS(pos);
  174.     else
  175.     res = curr_vw->vw_CursorPos;
  176.     if(!BUFFERP(tx))
  177.     tx = VAL(curr_vw->vw_Tx);
  178.     check_pos(VTX(tx), &res);
  179.     if(findcharnext(VTX(tx), VCHAR(ch), &res))
  180.     return(make_lpos(&res));
  181.     return(sym_nil);
  182. }
  183.  
  184. _PR VALUE cmd_find_prev_char(VALUE ch, VALUE pos, VALUE tx);
  185. DEFUN("find-prev-char", cmd_find_prev_char, subr_find_prev_char, (VALUE ch, VALUE pos, VALUE tx), V_Subr3, DOC_find_prev_char) /*
  186. ::doc:find_prev_char::
  187. find-prev-char CHAR [POS] [BUFFER]
  188.  
  189. Scans backwards from POS (or the cursor), in BUFFER, looking for a match
  190. with CHAR. Returns the position of the next match or nil.
  191. ::end:: */
  192. {
  193.     POS res;
  194.     DECLARE1(ch, CHARP);
  195.     if(POSP(pos))
  196.     res = VPOS(pos);
  197.     else
  198.     res = curr_vw->vw_CursorPos;
  199.     if(!BUFFERP(tx))
  200.     tx = VAL(curr_vw->vw_Tx);
  201.     check_pos(VTX(tx), &res);
  202.     if(findcharprev(VTX(tx), VCHAR(ch), &res))
  203.     return(make_lpos(&res));
  204.     return(sym_nil);
  205. }
  206.  
  207. _PR VALUE cmd_replace_regexp(VALUE re, VALUE tplt, VALUE pos, VALUE tx, VALUE nocase_p);
  208. DEFUN("replace-regexp", cmd_replace_regexp, subr_replace_regexp, (VALUE re, VALUE tplt, VALUE pos, VALUE tx, VALUE nocase_p), V_Subr5, DOC_replace_regexp) /*
  209. ::doc:replace_regexp::
  210. replace-regexp REGEXP TEMPLATE [POS] [BUFFER] [IGNORE-CASE-P]
  211.  
  212. If the text at POS or the cursor, matches REGEXP replace it with TEMPLATE,
  213. this is a string that can have the following escape characters,
  214.   \0, \&   whole string matched by REGEXP
  215.   \N       N'th parenthensized expression (1 <= N <= 9)
  216.  
  217. When IGNORE-CASE-P is non-nil the case of matched strings are ignored. Note
  218. that character classes are still case-significant.
  219. ::end:: */
  220. {
  221.     POS res;
  222.     DECLARE1(re, STRINGP);
  223.     DECLARE2(tplt, STRINGP);
  224.     if(POSP(pos))
  225.     res = VPOS(pos);
  226.     else
  227.     res = curr_vw->vw_CursorPos;
  228.     if(!BUFFERP(tx))
  229.     tx = VAL(curr_vw->vw_Tx);
  230.     check_pos(VTX(tx), &res);
  231.     if(!read_only(VTX(tx)) && replaceit(VTX(tx), VSTR(re), VSTR(tplt),
  232.                        &res, !NILP(nocase_p)))
  233.     return(make_lpos(&res));
  234.     return(sym_nil);
  235. }
  236.  
  237. _PR VALUE cmd_replace_string(VALUE orig, VALUE new, VALUE pos, VALUE tx);
  238. DEFUN("replace-string", cmd_replace_string, subr_replace_string, (VALUE orig, VALUE new, VALUE pos, VALUE tx), V_Subr4, DOC_replace_string) /*
  239. ::doc:replace_string::
  240. replace-string ORIGINAL NEW [POS] [BUFFER]
  241.  
  242. If the text at POS, or the cursor, matches ORIGINAL, replace it with the
  243. string NEW.
  244. ::end:: */
  245. {
  246.     POS res;
  247.     DECLARE1(orig, STRINGP);
  248.     DECLARE2(new, STRINGP);
  249.     if(POSP(pos))
  250.     res = VPOS(pos);
  251.     else
  252.     res = curr_vw->vw_CursorPos;
  253.     if(!BUFFERP(tx))
  254.     tx = VAL(curr_vw->vw_Tx);
  255.     check_pos(VTX(tx), &res);
  256.     if(!read_only(VTX(tx)) && replaceitstr(VTX(tx), VSTR(orig), VSTR(new), &res))
  257.     return(make_lpos(&res));
  258.     return(sym_nil);
  259. }
  260.  
  261. _PR VALUE cmd_regexp_expand(VALUE re, VALUE match, VALUE tplt, VALUE nocase_p);
  262. DEFUN("regexp-expand", cmd_regexp_expand, subr_regexp_expand, (VALUE re, VALUE match, VALUE tplt, VALUE nocase_p), V_Subr4, DOC_regexp_expand) /*
  263. ::doc:regexp_expand::
  264. regexp-expand REGEXP MATCHSTR TEMPLATE [IGNORE-CASE-P]
  265.  
  266. If REGEXP matches MATCHSTR then return the string made by expanding the
  267. string TEMPLATE in a similar way to in the function `replace-regexp'.
  268.  
  269. When IGNORE-CASE-P is non-nil the case of matched strings are ignored. Note
  270. that character classes are still case-significant.
  271. ::end:: */
  272. {
  273.     regexp *prog;
  274.     DECLARE1(re, STRINGP);
  275.     DECLARE2(match, STRINGP);
  276.     DECLARE3(tplt, STRINGP);
  277.     prog = regcomp(VSTR(re));
  278.     if(prog)
  279.     {
  280.     VALUE res = sym_nil;
  281.     if(regexec2(prog, VSTR(match), NILP(nocase_p) ? 0 : REG_NOCASE))
  282.     {
  283.         long replen = regsublen(prog, VSTR(tplt));
  284.         if(replen && (res = make_string(replen)))
  285.         regsub(prog, VSTR(tplt), VSTR(res));
  286.         else if(!replen)
  287.         res = string_dup("");
  288.         update_last_match(VSTR(match), prog, -1);
  289.     }
  290.     free(prog);
  291.     return(res);
  292.     }
  293.     return(NULL);
  294. }
  295.  
  296. _PR VALUE cmd_regexp_match(VALUE re, VALUE str, VALUE nocase_p);
  297. DEFUN("regexp-match", cmd_regexp_match, subr_regexp_match, (VALUE re, VALUE str, VALUE nocase_p), V_Subr3, DOC_regexp_match) /*
  298. ::doc:regexp_match::
  299. regexp-match REGEXP STRING [IGNORE-CASE-P]
  300.  
  301. Return t if REGEXP matches STRING.
  302.  
  303. When IGNORE-CASE-P is non-nil the case of matched strings are ignored. Note
  304. that character classes are still case-significant.
  305. ::end:: */
  306. {
  307.     regexp *prog;
  308.     DECLARE1(re, STRINGP);
  309.     DECLARE2(str, STRINGP);
  310.     prog = regcomp(VSTR(re));
  311.     if(prog)
  312.     {
  313.     VALUE res;
  314.     if(regexec2(prog, VSTR(str), NILP(nocase_p) ? 0 : REG_NOCASE))
  315.     {
  316.         update_last_match(VSTR(str), prog, -1);
  317.         res = sym_t;
  318.     }
  319.     else
  320.         res = sym_nil;
  321.     free(prog);
  322.     return(res);
  323.     }
  324.     return(NULL);
  325. }
  326.  
  327. _PR VALUE cmd_regexp_expand_line(VALUE re, VALUE tplt, VALUE pos, VALUE tx, VALUE nocase_p);
  328. DEFUN("regexp-expand-line", cmd_regexp_expand_line, subr_regexp_expand_line, (VALUE re, VALUE tplt, VALUE pos, VALUE tx, VALUE nocase_p), V_Subr5, DOC_regexp_expand_line) /*
  329. ::doc:regexp_expand_line::
  330. regexp-expand REGEXP TEMPLATE [POS] [BUFFER] [IGNORE-CASE-P]
  331.  
  332. If REGEXP matches the line at POS in BUFFER then return the string made
  333. by expanding the string TEMPLATE in a similar way to in the function
  334. `replace-regexp' and the variables `find-last-start-pos' and
  335. `find-last-end-pos' are set to the start and end of the match.
  336.  
  337. When IGNORE-CASE-P is non-nil the case of matched strings are ignored. Note
  338. that character classes are still case-significant.
  339. ::end:: */
  340. {
  341.     POS linepos;
  342.     regexp *prog;
  343.     DECLARE1(re, STRINGP);
  344.     DECLARE2(tplt, STRINGP);
  345.     if(!BUFFERP(tx))
  346.     tx = VAL(curr_vw->vw_Tx);
  347.     if(POSP(pos))
  348.     linepos = VPOS(pos);
  349.     else
  350.     linepos = *get_tx_cursor(VTX(tx));
  351.     if(check_line(VTX(tx), &linepos) && (prog = regcomp(VSTR(re))))
  352.     {
  353.     VALUE res = sym_nil;
  354.     u_char *line = VTX(tx)->tx_Lines[linepos.pos_Line].ln_Line;
  355.     if(regexec2(prog, line, NILP(nocase_p) ? 0 : REG_NOCASE))
  356.     {
  357.         long replen;
  358.         update_last_match(line, prog, linepos.pos_Line);
  359.         replen = regsublen(prog, VSTR(tplt));
  360.         if(replen && (res = make_string(replen)))
  361.         regsub(prog, VSTR(tplt), VSTR(res));
  362.         else if(!replen)
  363.         res = string_dup("");
  364.     }
  365.     free(prog);
  366.     return(res);
  367.     }
  368.     return(NULL);
  369. }
  370.  
  371. _PR VALUE cmd_regexp_match_line(VALUE re, VALUE pos, VALUE tx, VALUE nocase_p);
  372. DEFUN("regexp-match-line", cmd_regexp_match_line, subr_regexp_match_line, (VALUE re, VALUE pos, VALUE tx, VALUE nocase_p), V_Subr4, DOC_regexp_match_line) /*
  373. ::doc:regexp_match_line::
  374. regexp-match-line REGEXP [LINE-POS] [BUFFER] [IGNORE-CASE-P]
  375.  
  376. Attempts to match the regular-expression REGEXP to the line pointed to by
  377. LINE-POS and BUFFER. If the match succeeds t is returned and the variables
  378. `find-last-start-pos' and `find-last-end-pos' are set to the start and end
  379. of the match.
  380.  
  381. When IGNORE-CASE-P is non-nil the case of matched strings are ignored. Note
  382. that character classes are still case-significant.
  383. ::end:: */
  384. {
  385.     POS linepos;
  386.     regexp *prog;
  387.     DECLARE1(re, STRINGP);
  388.     if(!BUFFERP(tx))
  389.     tx = VAL(curr_vw->vw_Tx);
  390.     if(POSP(pos))
  391.     linepos = VPOS(pos);
  392.     else
  393.     linepos = *get_tx_cursor(VTX(tx));
  394.     if(check_line(VTX(tx), &linepos) && (prog = regcomp(VSTR(re))))
  395.     {
  396.     VALUE res;
  397.     u_char *line = VTX(tx)->tx_Lines[linepos.pos_Line].ln_Line;
  398.     if(regexec2(prog, line, NILP(nocase_p) ? 0 : REG_NOCASE))
  399.     {
  400.         update_last_match(line, prog, linepos.pos_Line);
  401.         res = sym_t;
  402.     }
  403.     else
  404.         res = sym_nil;
  405.     free(prog);
  406.     return(res);
  407.     }
  408.     return(NULL);
  409. }
  410.  
  411. _PR VALUE cmd_looking_at(VALUE re, VALUE vpos, VALUE tx, VALUE nocase_p);
  412. DEFUN("looking-at", cmd_looking_at, subr_looking_at, (VALUE re, VALUE vpos, VALUE tx, VALUE nocase_p), V_Subr4, DOC_looking_at) /*
  413. ::doc:looking_at::
  414. looking-at REGEXP [POS] [BUFFER] [IGNORE-CASE-P]
  415.  
  416. Returns t if REGEXP matches the text at POS. Only the text from POS to the
  417. end of the line is matched against.
  418. ::end:: */
  419. {
  420.     POS pos;
  421.     regexp *prog;
  422.     DECLARE1(re, STRINGP);
  423.     if(!BUFFERP(tx))
  424.     tx = VAL(curr_vw->vw_Tx);
  425.     if(POSP(vpos))
  426.     pos = VPOS(vpos);
  427.     else
  428.     pos = *get_tx_cursor(VTX(tx));
  429.     if(check_line(VTX(tx), &pos) && (prog = regcomp(VSTR(re))))
  430.     {
  431.     VALUE res;
  432.     u_char *line = VTX(tx)->tx_Lines[pos.pos_Line].ln_Line + pos.pos_Col;
  433.     if(regexec2(prog, line, ((NILP(nocase_p) ? 0 : REG_NOCASE)
  434.                  | ((pos.pos_Col == 0) ? 0 : REG_NOTBOL)))
  435.        && (prog->startp[0] == (char *)line))
  436.     {
  437.         update_last_match(VTX(tx)->tx_Lines[pos.pos_Line].ln_Line,
  438.                   prog, pos.pos_Line);
  439.         res = sym_t;
  440.     }
  441.     else
  442.         res = sym_nil;
  443.     free(prog);
  444.     return(res);
  445.     }
  446.     return(NULL);
  447. }
  448.  
  449. _PR VALUE cmd_match_start(VALUE exp);
  450. DEFUN("match-start", cmd_match_start, subr_match_start, (VALUE exp), V_Subr1, DOC_match_start) /*
  451. ::doc:match_start::
  452. match-start [EXPRESSION-INDEX]
  453.  
  454. Return the position which the EXPRESSION-INDEX'th parenthesised expression
  455. started at in the last successful regexp match. If EXPRESSION-INDEX is
  456. nil or 0 the start of the whole match is returned instead.
  457. The returned value will either be a position if the last match was in a
  458. buffer, or an integer if the last match was in a string (i.e. regexp-match).
  459. ::end:: */
  460. {
  461.     long i;
  462.     if(NUMBERP(exp))
  463.     {
  464.     i = VNUM(exp);
  465.     if((i >= NSUBEXP) || (i < 0))
  466.         return(signal_arg_error(exp, 1));
  467.     }
  468.     else
  469.     i = 0;
  470.     if(last_start[i] == NULL)
  471.     return(sym_nil);
  472.     i = last_start[i] - last_string;
  473.     if(last_line == -1)
  474.     return(make_number(i));
  475.     else
  476.     return(make_lpos2(i, last_line));
  477. }
  478.     
  479. _PR VALUE cmd_match_end(VALUE exp);
  480. DEFUN("match-end", cmd_match_end, subr_match_end, (VALUE exp), V_Subr1, DOC_match_end) /*
  481. ::doc:match_end::
  482. match-end [EXPRESSION-INDEX]
  483.  
  484. Return the position which the EXPRESSION-INDEX'th parenthesised expression
  485. ended at in the last successful regexp match. If EXPRESSION-INDEX is
  486. nil or 0 the end of the whole match is returned instead.
  487. The returned value will either be a position if the last match was in a
  488. buffer, or an integer if the last match was in a string (i.e. regexp-match).
  489. ::end:: */
  490. {
  491.     long i;
  492.     if(NUMBERP(exp))
  493.     {
  494.     i = VNUM(exp);
  495.     if((i >= NSUBEXP) || (i < 0))
  496.         return(signal_arg_error(exp, 1));
  497.     }
  498.     else
  499.     i = 0;
  500.     if(last_end[i] == NULL)
  501.     return(sym_nil);
  502.     i = last_end[i] - last_string;
  503.     if(last_line == -1)
  504.     return(make_number(i));
  505.     else
  506.     return(make_lpos2(i, last_line));
  507. }
  508.  
  509. _PR VALUE cmd_regexp_quote(VALUE str);
  510. DEFUN("regexp-quote", cmd_regexp_quote, subr_regexp_quote, (VALUE str), V_Subr1, DOC_regexp_quote) /*
  511. ::doc:regexp_quote::
  512. regexp-quote STRING
  513.  
  514. Returns a new version of STRING, any characters which the regexp routines
  515. treat specially (asterisks, square brackets, etc...) is quoted by the escape
  516. character `\'. If the STRING does not contain any regexp meta-characters
  517. it is returned as-is (un-copied).
  518. ::end:: */
  519. {
  520.     u_char *buf, *s;
  521.     int buflen = 128, slen, i = 0;
  522.     bool quoted = FALSE;
  523.     VALUE res = NULL;
  524.     DECLARE1(str, STRINGP);
  525.     s = VSTR(str);
  526.     slen = STRING_LEN(str);
  527.     buf = str_alloc(buflen);
  528.     if(!buf)
  529.     goto error;
  530.     while(slen-- > 0)
  531.     {
  532.     u_char c;
  533.     /* Ensure string is long enough, this saves doing this twice. */
  534.     if(i + 2 >= buflen)
  535.     {
  536.         int newlen = buflen * 2;
  537.         u_char *newbuf = str_alloc(newlen);
  538.         if(!newbuf)
  539.         goto error;
  540.         memcpy(newbuf, buf, i);
  541.         str_free(buf);
  542.         buf = newbuf;
  543.         buflen = newlen;
  544.     }
  545.     switch(c = *s++)
  546.     {
  547.     case '*':
  548.     case '+':
  549.     case '?':
  550.     case '.':
  551.     case '[':
  552.     case ']':
  553.     case '(':
  554.     case ')':
  555.     case '|':
  556.     case '^':
  557.     case '$':
  558.     case '\\':     /* do I want to do this? */
  559.         /* quote this character */
  560.         buf[i++] = '\\';
  561.         buf[i++] = c;
  562.         quoted = TRUE;
  563.         break;
  564.     default:
  565.         buf[i++] = c;
  566.         break;
  567.     }
  568.     }
  569.     if(!quoted)
  570.     res = str;
  571.     else
  572.     res = string_dupn(buf, i);
  573. error:
  574.     if(buf)
  575.     str_free(buf);
  576.     return(res);
  577. }
  578.  
  579. bool
  580. findnext(TX *tx, u_char * re, POS *pos, bool nocase)
  581. {
  582.     bool rc = FALSE;
  583.     regexp *prog;
  584.     prog = regcomp(re);
  585.     if(prog)
  586.     {
  587.     LINE *line = tx->tx_Lines + pos->pos_Line;
  588.     long index;
  589.     if(pos->pos_Col >= line->ln_Strlen)
  590.         index = line->ln_Strlen - 1;
  591.     else
  592.         index = pos->pos_Col;
  593.     while(!rc && (tx->tx_NumLines > pos->pos_Line))
  594.     {
  595.         if(regexec2(prog, line->ln_Line + index, (index ? REG_NOTBOL : 0) | (nocase ? REG_NOCASE : 0)))
  596.         rc = TRUE;
  597.         else
  598.         {
  599.         pos->pos_Line++;
  600.         line++;
  601.         index = 0;
  602.         }
  603.     }
  604.     if(rc)
  605.     {
  606.         update_last_match(line->ln_Line, prog, pos->pos_Line);
  607.         pos->pos_Col = prog->startp[0] - ((char *)line->ln_Line);
  608.     }
  609.     free(prog);
  610.     }
  611.     return(rc);
  612. }
  613.  
  614. bool
  615. findprev(TX *tx, u_char *re, POS *pos, bool nocase)
  616. {
  617.     bool rc = FALSE;
  618.     regexp *prog = regcomp(re);
  619.     if(prog)
  620.     {
  621.     LINE *line = tx->tx_Lines + pos->pos_Line;
  622.     long index;
  623.     u_char *found = NULL;
  624.     regexp last_match;        /* holds last match positions */
  625.     if(pos->pos_Col >= line->ln_Strlen)
  626.         pos->pos_Col = line->ln_Strlen - 1;
  627.     /*
  628.      * This makes the forward searching regexp matcher go backwards.
  629.      * we simply find as many matches as we can on a line, then return
  630.      * the last (taking the "start" position into account)
  631.      */
  632.     while(!rc && (pos->pos_Line >= 0))
  633.     {
  634.         index = 0;
  635.         while(regexec2(prog, line->ln_Line + index,
  636.                (index ? REG_NOTBOL : 0) | (nocase ? REG_NOCASE : 0)))
  637.         {
  638.         long oindex = index;
  639.         if(((prog->startp)[0] - (char *)line->ln_Line) > pos->pos_Col)
  640.             break;
  641.         found = (prog->startp)[0];
  642.         memcpy(&last_match, prog, sizeof(last_match));
  643.         rc = TRUE;
  644.         index = (prog->endp)[0] - (char *)line->ln_Line;
  645.         if(oindex >= index)
  646.         {
  647.             if(index >= line->ln_Strlen)
  648.             break;
  649.             else
  650.             index = oindex + 1;
  651.         }
  652.         }
  653.         if(!found)
  654.         {
  655.         line--;
  656.         pos->pos_Line--;
  657.         pos->pos_Col = line->ln_Strlen - 1;
  658.         index = 0;
  659.         }
  660.     }
  661.     if(rc)
  662.     {
  663.         update_last_match(line->ln_Line, &last_match, pos->pos_Line);
  664.         pos->pos_Col = found - line->ln_Line;
  665.     }
  666.     free(prog);
  667.     }
  668.     return(rc);
  669. }
  670.  
  671. bool
  672. findstrnext(TX *tx, u_char *str, POS *pos)
  673. {
  674.     LINE *line = tx->tx_Lines + pos->pos_Line;
  675.     if(pos->pos_Col >= line->ln_Strlen)
  676.     {
  677.     pos->pos_Col = 0;
  678.     pos->pos_Line++;
  679.     line++;
  680.     }
  681.     while(tx->tx_NumLines > pos->pos_Line)
  682.     {
  683.     u_char *match = strstr(line->ln_Line + pos->pos_Col, str);
  684.     if(match)
  685.     {
  686.         pos->pos_Col = match - line->ln_Line;
  687.         return(TRUE);
  688.     }
  689.     pos->pos_Line++;
  690.     pos->pos_Col = 0;
  691.     line++;
  692.     }
  693.     return(FALSE);
  694. }
  695.  
  696. bool
  697. findstrprev(TX *tx, u_char *str, POS *pos)
  698. {
  699.     LINE *line = tx->tx_Lines + pos->pos_Line;
  700.     if(pos->pos_Col >= line->ln_Strlen)
  701.     pos->pos_Col = line->ln_Strlen - 1;
  702.     while(pos->pos_Line >= 0)
  703.     {
  704.     u_char *match = mystrrstrn(line->ln_Line, str, pos->pos_Col + 1);
  705.     if(match)
  706.     {
  707.         pos->pos_Col = match - line->ln_Line;
  708.         return(TRUE);
  709.     }
  710.     line--;
  711.     pos->pos_Line--;
  712.     pos->pos_Col = line->ln_Strlen - 1;
  713.     }
  714.     return(FALSE);
  715. }
  716.  
  717. bool
  718. findcharnext(TX *tx, u_char c, POS *pos)
  719. {
  720.     LINE *line = tx->tx_Lines + pos->pos_Line;
  721.     if(pos->pos_Col >= line->ln_Strlen)
  722.     {
  723.     pos->pos_Col = 0;
  724.     pos->pos_Line++;
  725.     line++;
  726.     }
  727.     while(tx->tx_NumLines > pos->pos_Line)
  728.     {
  729.     u_char *match = strchr(line->ln_Line + pos->pos_Col, c);
  730.     if(match)
  731.     {
  732.         pos->pos_Col = match - line->ln_Line;
  733.         return(TRUE);
  734.     }
  735.     pos->pos_Line++;
  736.     pos->pos_Col = 0;
  737.     line++;
  738.     }
  739.     return(FALSE);
  740. }
  741.  
  742. bool
  743. findcharprev(TX *tx, u_char c, POS *pos)
  744. {
  745.     LINE *line = tx->tx_Lines + pos->pos_Line;
  746.     if(pos->pos_Col >= line->ln_Strlen)
  747.     pos->pos_Col = line->ln_Strlen - 1;
  748.     while(pos->pos_Line >= 0)
  749.     {
  750.     u_char *match = mystrrchrn(line->ln_Line, c, pos->pos_Col + 1);
  751.     if(match)
  752.     {
  753.         pos->pos_Col = match - line->ln_Line;
  754.         return(TRUE);
  755.     }
  756.     line--;
  757.     pos->pos_Line--;
  758.     pos->pos_Col = line->ln_Strlen - 1;;
  759.     }
  760.     return(FALSE);
  761. }
  762.  
  763. bool
  764. replaceit(TX *tx, u_char *re, u_char *str, POS *pos, bool nocase)
  765. {
  766.     bool rc = FALSE;
  767.     regexp *prog;
  768.     if((prog = regcomp(re)) && pad_pos(tx, pos))
  769.     {
  770.     u_char *line = tx->tx_Lines[pos->pos_Line].ln_Line;
  771.     u_char *posstr = line + pos->pos_Col;
  772.     if(((*re != '^') || (pos->pos_Col == 0))
  773.        && regexec2(prog, posstr, nocase ? REG_NOCASE : 0)
  774.        && (pos->pos_Col == (prog->startp[0] - ((char *)line))))
  775.     {
  776.         long replen = regsublen(prog, str);
  777.         u_char *repstr;
  778.         if(replen && (repstr = str_alloc(replen)))
  779.         {
  780.         POS end;
  781.         regsub(prog, str, repstr);
  782.         end.pos_Line = pos->pos_Line;
  783.         end.pos_Col = pos->pos_Col + (prog->endp[0] - prog->startp[0]);
  784.         undo_record_deletion(tx, pos, &end);
  785.         delete_chars(tx, pos, prog->endp[0] - prog->startp[0]);
  786.         flag_deletion(tx, pos, &end);
  787.         end.pos_Col = pos->pos_Col + (replen - 1);
  788.         undo_record_insertion(tx, pos, &end);
  789.         flag_insertion(tx, pos, &end);
  790.         rc = insert_str(tx, repstr, pos);
  791.         str_free(repstr);
  792.         }
  793.         else if(replen)
  794.         mem_error();
  795.     }
  796.     else
  797.         cmd_signal(sym_error, LIST_1(MKSTR("Regexp doesn't match replace position")));
  798.     free(prog);
  799.     }
  800.     return(rc);
  801. }
  802.  
  803. bool
  804. replaceitstr(TX *tx, u_char *orig, u_char *new, POS *pos)
  805. {
  806.     bool rc = FALSE;
  807.     u_char *line;
  808.     line = tx->tx_Lines[pos->pos_Line].ln_Line + pos->pos_Col;
  809.     if(mystrcmp(orig, line))
  810.     {
  811.     POS end;
  812.     end.pos_Line = pos->pos_Line;
  813.     end.pos_Col = pos->pos_Col + strlen(orig);
  814.     undo_record_deletion(tx, pos, &end);
  815.     delete_chars(tx, pos, strlen(orig));
  816.     flag_deletion(tx, pos, &end);
  817.     end.pos_Col = pos->pos_Col + strlen(new);
  818.     undo_record_insertion(tx, pos, &end);
  819.     flag_insertion(tx, pos, &end);
  820.     rc = insert_str(tx, new, pos);
  821.     }
  822.     else
  823.     cmd_signal(sym_error, LIST_1(MKSTR("String doesn't match replace position")));
  824.     return(rc);
  825. }
  826.  
  827. void
  828. regerror(char *err)
  829. {
  830. #if 0
  831.     message(err);
  832. #else
  833.     cmd_signal(sym_regexp_error,
  834.            list_2(MKSTR("Regular-expression"), string_dup(err)));
  835. #endif
  836. }
  837.  
  838. /*
  839.  * These functions return TRUE if the strings match, if str1 ends before
  840.  * str2 they are still considered to be matching.
  841.  */
  842. bool
  843. mystrcmp(u_char *str1, u_char *str2)
  844. {
  845.     while(*str2 && *str1)
  846.     {
  847.     if(*str2++ != *str1++)
  848.         return(FALSE);
  849.     }
  850.     if(*str2 || (*str2 == *str1))
  851.     return(TRUE);
  852.     return(FALSE);
  853. }
  854.  
  855. /*
  856.  * find last occurrence of str2 in str1
  857.  */
  858. u_char *
  859. mystrrstrn(u_char *str1, u_char *str2, int str1Len)
  860. {
  861.     int str2len = strlen(str2);
  862.     register int i;
  863.     u_char c = str2[str2len - 1];
  864.     for(i = str1Len - 1; i >= str2len; i--)
  865.     {
  866.     if(str1[i] == c)
  867.     {
  868.         u_char *tmp = (str1 + i) - (str2len - 1);
  869.         if(mystrcmp(str2, tmp))
  870.         return(tmp);
  871.     }
  872.     }
  873.     return(NULL);
  874. }
  875.  
  876. u_char *
  877. mystrrchrn(u_char *str, u_char c, int strLen)
  878. {
  879.     register u_char *s = str + strLen;
  880.     while(s != str)
  881.     {
  882.     if(*(--s) == c)
  883.         return(s);
  884.     }
  885.     return(NULL);
  886. }
  887.  
  888. void
  889. find_init(void)
  890. {
  891.     INTERN(sym_regexp_error, "regexp-error");
  892.     cmd_put(sym_regexp_error, sym_error_message, MKSTR("Regexp error"));
  893.     ADD_SUBR(subr_find_next_regexp);
  894.     ADD_SUBR(subr_find_prev_regexp);
  895.     ADD_SUBR(subr_find_next_string);
  896.     ADD_SUBR(subr_find_prev_string);
  897.     ADD_SUBR(subr_find_next_char);
  898.     ADD_SUBR(subr_find_prev_char);
  899.     ADD_SUBR(subr_replace_string);
  900.     ADD_SUBR(subr_replace_regexp);
  901.     ADD_SUBR(subr_regexp_expand);
  902.     ADD_SUBR(subr_regexp_match);
  903.     ADD_SUBR(subr_regexp_expand_line);
  904.     ADD_SUBR(subr_regexp_match_line);
  905.     ADD_SUBR(subr_looking_at);
  906.     ADD_SUBR(subr_match_start);
  907.     ADD_SUBR(subr_match_end);
  908.     ADD_SUBR(subr_regexp_quote);
  909. }
  910.